#pragma once
#include <game/Config.h>

#include <Atom.h>
#include <xml/Stream.h>
#include <xml/Saver.h>
#include <map>

ARELLO_GAME_NAMESPACE_DECL_START

template<class T, class  Key>
class XMLProperties: public xml::ObjectANSI
{
	class ValueLoader: public xml::ObjectANSI, public xml::ObjectUnicode
	{		
	protected:
		virtual bool on_attribute(xml::Provider * prov, const std::string & name, const std::string & value )
		{
			std::wstring wstr( name.begin(), name.end() );
			if( wstr == value_name)
			{
				return converter::convert( value, &val );
			}
			else if( wstr == property_name)
			{
				return converter::convert( value, &key );
			}
			return false;
		}

		virtual bool on_attribute(xml::Provider * prov, const std::wstring & name, const std::wstring & value )
		{
			if( name == value_name)
			{
				return converter::convert( value, &val );
			}
			else if( name == property_name)
			{
				return converter::convert( value, &key );
			}
			return false;
		}

		virtual void on_save_attributes( xml::SaverBase * saver )const
		{
			std::string strkey,strval;
			converter::convert( key, &strkey );
			converter::convert( val, &strval );
			saver->save_attribute( property_name, strkey );
			saver->save_attribute( value_name, strval );
		}

	public:
		ValueLoader( const std::string & property_name, const std::string & value_name )
		{
			converter::convert( property_name, &(this->property_name) );
			converter::convert( value_name, &(this->value_name) );
		};

		T val;
		Key key;
	private:
		std::wstring property_name;
		std::wstring value_name;
	};

public:
	typedef std::map< Key, T > Values;

	const Values & get_values()const
	{
		return values;
	}

	bool has_values()const
	{
		if(values.empty())
			return false;
		else return true;
	}
	
	template<class U>
	class CachedValues : public std::map<Key, U>
	{};
	
	XMLProperties( const std::string & collection_name, 
			const std::string & property_name, 
			const std::string & value_name):
		collection_name( collection_name ),
		property_name( property_name ),
		value_name( value_name )
	{
	}

	template< class U >
	void get( const Key & key, U * val )const
	{
		typename Values::const_iterator it = values.find( key );
		if( it != values.end() )
			converter::convert( it->second, val );
		else
			converter::convert( T(), val );
	}

	bool has_double_value(const Key & key)const
	{
		typename CachedValues<double>::const_iterator it = doubles.find( key );
		if( it != doubles.end() )
			return true;
		
		return false;
	}

	void get( const Key & key, T * val )const
	{
		typename Values::const_iterator it = values.find( key );
		if( it != values.end() )
			*val = it->second;
		else
			*val = T();
	}
	
	void get( const Key & key, double * val )const
	{
		typename CachedValues<double>::const_iterator it = doubles.find( key );
		if( it != doubles.end() )
			*val = it->second;
		else
		{
			typename Values::const_iterator it = values.find( key );
			if( it != values.end() )
				converter::convert( it->second, val );
			else
				converter::convert( T(), val );
			
			doubles[key] = *val;
		}
	}

	void get( const Key & key, int * val )const
	{
		double dval = *val;
		get(key, &dval);
		*val = int(dval);
	}
	
	template< class U >
	void set( const Key & key, const U & v )
	{
		std::wstring wstr;
		converter::convert(v, &wstr);
		values[key] = wstr;
	}

	void set( const Key & key, const T & v )
	{
		values[key] = v;
	}

	void set( const Key & key, const double & v )
	{
		doubles[key] = v;
	}
	
	void set( const Key & key, const int & v )
	{
		doubles[key] = v;
	}

protected:
	virtual bool on_node(xml::Provider * prov, const std::string & name )
	{
		if(!doubles.empty())
		{
			typename CachedValues<double>::iterator it = doubles.begin();
			for(; it != doubles.end(); ++it)
			{
				T str;
				converter::convert(it->second, &str);
				values[it->first] = str;
			}
			
			doubles.clear();
		}
		
		if( name ==	collection_name)
		{
			ValueLoader loader(property_name, value_name);
			if( prov->parse_object( &loader ) )
			{
				values[ loader.key ] = loader.val;
				return true;
			}
		}
		return false;
	}

	virtual void on_save_nodes( xml::SaverBase * saver )const
	{
		if(!doubles.empty())
		{
			typename CachedValues<double>::iterator it = doubles.begin();
			for(; it != doubles.end(); ++it)
			{
				T str;
				converter::convert(it->second, &str);
				values[it->first] = str;
			}

			doubles.clear();
		}

		typename Values::const_iterator it = values.begin();
		typename Values::const_iterator end = values.end();
		for( ; it != end; ++it )
		{
			ValueLoader loader( property_name, value_name );
			loader.key = it->first;
			loader.val = it->second;
			saver->save_node( collection_name, &loader );
		}
	}

	mutable Values values;
	std::string collection_name;
	std::string property_name;
	std::string value_name;
	
	mutable CachedValues<double> doubles;
};

class Properties: public XMLProperties<std::wstring, liba::Atom>
{
public:
	Properties():
		XMLProperties<std::wstring, liba::Atom>( "Property", "Name", "Value" )
	{
	}
	template<class T>
	T get_value(Atom key, T defval)const
	{
		get( key, &defval );
		return defval;
	}
	template<class T>
	void set_value(Atom key, T val)
	{
		set( key, val );
	}
};

ARELLO_GAME_NAMESPACE_DECL_END